<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <link rel="stylesheet" href="./stylus/style.css" />
  <script src="./lib/spark-md5.js"></script>
  <!-- 引入样式 -->
  <link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.8/theme-chalk/index.css" rel="stylesheet">
  <!-- import Vue before Element -->
  <!-- <script src="https://unpkg.com/vue/dist/vue.js"></script> -->
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.2/vue.js"></script>
  <!-- import JavaScript -->
  <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.8/index.js"></script>
  <style>
    .main-class {
      margin-bottom: 2rem;
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="container">
      <div class="main-class">
        <div class="file-layer">
          <input type="file" name="file" id="file" @change="watchFileCallback" />
        </div>
        <div>
          <el-button type="primary" size="small" id="upload" @click="fnCallUpload">等额分割上传</el-button>
          <el-button type="primary" size="small" id="upload" @click="fnSlowStart">慢启动上传</el-button>
          <el-button type="primary" size="small" id="merge" @click="fnCallMerge">合并文件</el-button>
          <el-button type="primary" size="small" id="pause" @click="fnCallPause">暂停上传</el-button>
          <el-button type="primary" size="small" id="resume" @click="fnCallRestart">恢复上传</el-button>
        </div>
      </div>
      <div class="" v-if="source">
        <img style="width: 240px; object-fit: scale-down;" :src="source" alt="" srcset="">
      </div>
      <div>
        <el-progress :percentage="percentage"></el-progress>
      </div>
    </div>
  </div>
  <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js"></script>
  <script src="./scripts/request.js"></script>
  <script src="./scripts/utils.js"></script>
  <script>
    const worker = new Worker("./scripts/hash_worker.js");
    axios.interceptors.response.use(function (response) {
      // 对响应数据做点什么
      return response.data;
    }, function (error) {
      // 对响应错误做点什么
      return Promise.reject(error);
    });

    new Vue({
      el: "#app",
      data: {
        payload: null,
        source: null,
        Standard: 30,
        percentage: 0
      },
      methods: {
        watchFileCallback (event) {
          const [file] = event.target.files;
          if (file) {
            const {
              name,
              size,
              type
            } = file
            if ((type || "").includes("image")) {
              // const reader = new FileReader();
              // reader.readAsDataURL(file);
              // reader.onload = ({ currentTarget }) => {
              //   this.source = currentTarget.result
              // };
            }
            this.payload = {
              name,
              file,
              size,
            };
          }
        },
        async fnCallUpload () {
          if (this.payload) {
            const {
              file,
              name,
              size
            } = this.payload;
            const fileChunkSlice = createFileChunk(file);
            console.time("hash_accept");
            const hash_accept = await hashSendFnCreate(fileChunkSlice, size);
            console.timeEnd("hash_accept");
            console.log(fileChunkSlice.length, hash_accept);
            this.payload.hash_name = `${hash_accept}_${name}`;

            const message = await axios.post("/verify", {
              hash_accept,
              file_name: name
            });

            if (message.is_exist) {
              this.$message.success("该文件已经上传");
            } else {
              const sureSendSlice = fileChunkSlice.filter(
                (val) => !message.list_exist.includes(`${val.index}_${hash_accept}`)
              );
              const requestSlice = dataWrapper(sureSendSlice, hash_accept);
              if (requestSlice.length) {
                this.limitSendSameTime(requestSlice, 5);
              } else {
                this.$message.success("该文件已经上传");
              }
            }
          } else {
            this.$message.error("请先选择上传文件");
          }
        },
        async fnCallMerge () {
          if (this.payload && this.payload.hash_name) {
            axios.post('/merge', {
              file_name: this.payload.hash_name
            }).then((res) => {
              this.$message.success(res.desc || "该文件合并成功");
            });
          }
        },
        async fnCallPause () {
          console.log("====fnCallPause====");
        },
        async fnCallRestart () {
          console.log("====fnCallRestart====");
        },
        async fnSlowStart () {
          if (this.payload) {
            const {
              file,
              name,
              size
            } = this.payload;
            const fileChunkSlice = createFileChunk(file);
            console.time("hash_accept");
            const hash_accept = await hashSendFnCreate(fileChunkSlice);
            this.payload.hash_name = `${hash_accept}_${name}`;

            const fileSize = file.size;
            let current = 0,
              count = 0,
              offset = 2 * 1024 * 1024;
            while (current < fileSize) {
              // 计算最后一次传输数据
              let endSend = current + offset;
              endSend = endSend >= fileSize ? fileSize : endSend;
              const chunk = file.slice(current, endSend);

              const payload = new FormData();
              payload.append("file", chunk);
              payload.append("hash", `${count}_${hash_accept}`);
              payload.append("file_name", name);
              payload.append("size", size);
              payload.append("hash_accept", hash_accept);
              let start = new Date().getTime();
              await axios.post("/file", payload, {
                headers: {
                  "Content-Type": "application/x-www-form-urlencoded",
                },
                onUploadProgress: progressEvent => {
                  const { loaded, total } = progressEvent;
                  this.percentage = this.progressEventSolve(current, loaded, total, fileSize);
                }
              });
              // 在修改上传数据量之前，先计算得出下次上传初始位置
              current += offset;
              const now = new Date().getTime();
              const time = ((now - start) / 1000).toFixed(4);
              let rate = this.Standard / time;
              // 速率有最大和最小 可以考虑更平滑的过滤 比如1/tan 
              if (rate <= 0.25) rate = 0.25;
              if (rate >= 4) rate = 4;
              // 新的切片大小等比变化
              console.log(`切片${count}大小是${this.showFormat(offset)},耗时${time}秒，是30秒的${rate}倍`);
              console.log(`上传总数据:${size}；下次上传数据量，修正大小为${this.showFormat(offset * rate)}`);

              offset = parseInt(offset * rate);
              count++;
            }

          } else {
            this.$message.error("请先选择上传文件");
          }
        },
        progressEventSolve (current, loaded, total, fileSize) {
          const dataSaved = current + (total * (loaded / total));
          const progress = (dataSaved * 100 / fileSize).toFixed(1);
          return parseFloat(progress);
        },
        showFormat (num) {
          const k_bit = 1024,
            m_bit = 1024 * 1024,
            g_bit = 1024 * 1024 * 1024;
          if (num > g_bit) {
            return (num / g_bit).toFixed(2) + 'GB';
          }
          if (num > m_bit) {
            return (num / m_bit).toFixed(2) + 'MB';
          }
          if (num > k_bit) {
            return (num / k_bit).toFixed(2) + 'KB';
          }
          return num + 'B';
        },
        dataSolve (rate = 1) {
          let template = 1 / rate;
          if (template <= 1) {
            return Math.max(template, 1);
          } else {
            return Math.ceil(template / 10);
          }
        },
        limitSendSameTime (requestSlice = [], limit = 4) {
          let marked = 0,
            isClosed = true;
          const fnStart = () => {
            while (marked < limit && requestSlice.length) {
              const payload = requestSlice.shift();
              marked++;
              if (!payload.isCrossed || payload.isCrossed <= 2) {
                axios.post("/file", payload, {
                  headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                  },
                }).then((res) => {
                  marked--;
                  if (requestSlice.length) {
                    fnStart();
                  } else {
                    if (isClosed) {
                      isClosed = false;
                      this.$message.success("该文件已上传成功");
                    }
                  }
                }).catch(error => {
                  marked--;
                  payload.isCrossed = (payload.isCrossed || 0) + 1;
                  requestSlice.unshift(payload);
                  fnStart();
                })
              } else {
                requestSlice.push(payload);
                console.log("终止该请求");
                if (requestSlice.every(item => item.isCrossed)) {
                  console.log(requestSlice);
                  if (isClosed) {
                    isClosed = false;
                    this.$message.error("本次请求终止，且部分数据未上传");
                  }
                }
              }
            }
          }

          fnStart();
        }
      },
    })
  </script>
</body>

</html>